package top.linuyx.service.impl;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import top.linuyx.exception.GlobalException;
import top.linuyx.mapper.AccountMapper;
import top.linuyx.mapper.AccountRoleMapper;
import top.linuyx.mapper.RoleMapper;
import top.linuyx.mapper.UserMapper;
import top.linuyx.pojo.entity.AccountEntity;
import top.linuyx.pojo.param.AccountLoginParam;
import top.linuyx.pojo.param.AccountRegisterParam;
import top.linuyx.service.AccountService;
import top.linuyx.utils.LinuyxUtils;

import java.util.List;

/**
 * @Author Linuyx
 * @Description AccountService实现类 处理:注册、查询账户等业务
 * @Date Created in 2021-04-09 8:54
 */

@Service
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountMapper accountMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private RoleMapper roleMapper;

    @Autowired
    private AccountRoleMapper accountRoleMapper;

    /**
     * 登录角色
     */
    public static ThreadLocal<String> role = new ThreadLocal<>();

    /**
     * 注册(这里有BUG:事务无法正常回滚)
     *
     * @param registerParam 注册请求体
     */
    @Transactional
    @Override
    public void register(AccountRegisterParam registerParam) {
        //生成随机盐(6位)
        String salt = LinuyxUtils.getSalt(6);
        //将随机盐保存到请求体中
        registerParam.setSalt(salt);
        //明文密码进行 md5 + salt + hash散列(1024次)
        Md5Hash md5Hash = new Md5Hash(registerParam.getPassword(), salt, 1024);

        //用加密后的密码替换明密码
        registerParam.setPassword(md5Hash.toHex());

        synchronized (this) {
            //根据用户名查询账户
            AccountEntity account = getAccountByUsername(registerParam.getUsername());

            //判断账户是否存在
            if (account != null) {
                throw new GlobalException("账户已存在");
            }

            //添加账户
            accountMapper.register(registerParam);
        }

        //添加用户信息,并用账号id关联用户信息(信息中只赋值了用户信息所关联的账户id)
        userMapper.insertUser(registerParam.getId());

        //根据角色名称查询对应角色id
        List<Long> roles = roleMapper.listIdByName(registerParam.getRoles());
        //给账户添加角色
        accountRoleMapper.insertAccountRole(registerParam.getId(), roles);
    }

    /**
     * 登录
     *
     * @param loginParam 登录请求体
     */
    @Override
    public void login(AccountLoginParam loginParam) {
        Subject subject = SecurityUtils.getSubject();
        if (subject.getPrincipal() != null) {
            throw new GlobalException("请退出当前用户再尝试登录");
        }

        //将登录角色添加到ThreadLocal中
        role.set(loginParam.getRole());

        try {
            subject.login(new UsernamePasswordToken(loginParam.getUsername(), loginParam.getPassword()));
        } catch (UnknownAccountException ex) {
            throw new GlobalException("用户名不存在");
        } catch (IncorrectCredentialsException ex) {
            throw new GlobalException("密码错误");
        }
    }

    /**
     * 根据用户名查询账户
     *
     * @param username 用户名
     * @return
     */
    @Override
    public AccountEntity getAccountByUsername(String username) {
        return accountMapper.getAccountByUsername(username);
    }

    /**
     * 根据用户名和角色查询账户
     *
     * @param username 用户名
     * @param role     角色
     * @return AccountEntity
     */
    @Override
    public AccountEntity getAccountByUsernameAndRole(String username, String role) {
        return accountMapper.getAccountByUsernameAndRole(username, role);
    }

}
